﻿using System.Collections.Generic;
using System.IO;
using Jayden.Dll.Core.Extensions;
using Jayden.Dll.Web.Enums;
using Jayden.Dll.Web.Events;
using Jayden.Dll.Web.HtmlControls;
using Jayden.Dll.Web.Json;

namespace Jayden.Dll.Web.Controls.Admin
{
	public class FileManagerDiv : Div
	{


		public delegate void OnFileDeleted(HttpHandlerContext context);
		public delegate void OnDirectoryCreated(HttpHandlerContext context);

		public class FileCommandHandler : HttpCommandHandler
		{
			public FileCommandHandler() : this("DeleteFiles", "CreateDirectory") { }
			public FileCommandHandler(string deleteFilesCommand, string createDirectoryCommand)
				: base(deleteFilesCommand, createDirectoryCommand)
			{
				RegisterEvent("FileDeleted");
				RegisterEvent("DirectoryCreated");
			}
			public event OnFileDeleted FileDeleted { add { AddEvent<OnFileDeleted>("FileDeleted", value); } remove { RemoveEvent<OnFileDeleted>("FileDeleted", value); } }
			public event OnDirectoryCreated DirectoryCreated { add { AddEvent<OnDirectoryCreated>("DirectoryCreated", value); } remove { RemoveEvent<OnDirectoryCreated>("DirectoryCreated", value); } }

			protected override void ProcessCommand(HttpHandlerContext context, string command)
			{
				string path = context.Request["path"];
				if (command == "CreateDirectory")
				{
					string name = context.Request["name"];
					string physicalPath = context.MapPath(path + "\\" + name);
					Directory.CreateDirectory(physicalPath);
					Invoke("DirectoryCreated", context);
				}
				else if (command == "DeleteFiles")
				{
					string[] files = context.Request["files"].Split(',');
					foreach(string file in files)
					{
						string physPath = context.MapPath(file);
						if (Directory.Exists(physPath))
							Directory.Delete(physPath);
						else
							File.Delete(physPath);
					}
					Invoke("FileDeleted", context);
				}
			}
		}


		private string m_CurrentVirtualDirectory = "/";

		private FileDataTable m_Files;
		private UploadForm m_UploadForm;

		private Input m_Name;
		private Button m_Create;
		private Button m_Delete;

		private Form m_DeleteFileForm;
		private Form m_CreateDirectoryForm;

		public override void Load(System.Xml.XmlNode node, HtmlControlLoader loader)
		{
			base.Load(node, loader);

			m_Files = GetElementByServerId<FileDataTable>("files", true);
			m_UploadForm = GetElementByServerId<UploadForm>("uploadForm", true);
			m_UploadForm.FilesSaved += new UploadForm.OnFilesSaved(FilesSaved);

			m_Name = GetElementByServerId<Input>("name", true, true);
			m_Create = GetElementByServerId<Button>("create", true, "command");
			m_Delete = GetElementByServerId<Button>("delete", true, "command");

			m_DeleteFileForm = GetElementByServerId<Form>("deleteFileForm", true);
			m_CreateDirectoryForm = GetElementByServerId<Form>("createDirectoryForm", true);
		}

		void FilesSaved(HttpHandlerContext context)
		{
			FillFromContext(context);
			if (context.Json != null)
			{
				JsonObject table = new JsonObject();
				table.Add("html", m_Files.ToInnerHtml(context.Translator));
				context.Json.Add(m_Files.Id, table);
			}
		}

		public override void ProcessJsonRequest(HttpHandlerContext context)
		{
			FileCommandHandler fileHandler = context.HandlerManager.GetHandler<FileCommandHandler>();
			fileHandler.DirectoryCreated += new OnDirectoryCreated(DirectoryCreated);
			fileHandler.FileDeleted += new OnFileDeleted(FileDeleted);

			if (context.Request["path"] != null)
				m_CurrentVirtualDirectory = context.Request["path"];
			if (string.IsNullOrWhiteSpace(m_CurrentVirtualDirectory))
				m_CurrentVirtualDirectory = "/";

			m_UploadForm.ServerSaveDirectory = m_CurrentVirtualDirectory;

			string rootPath = context.MapPath("/");
			m_Files.RootPathLength = rootPath.Length - 1;

			base.ProcessJsonRequest(context);
		}

		private void FillFromContext(HttpHandlerContext context)
		{
			string basePhysicalPath = context.MapPath(m_CurrentVirtualDirectory);
			List<string> values = new List<string>();
			values.AddRange(Directory.GetDirectories(basePhysicalPath, "*", SearchOption.TopDirectoryOnly));
			values.AddRange(Directory.GetFiles(basePhysicalPath, "*", SearchOption.TopDirectoryOnly));

			m_Files.Clear();
			m_Files.AddCaption("Current path: " + m_CurrentVirtualDirectory);
			Th[] ths = m_Files.AddHeaders();
			ths[0].AddControl(new Input(HtmlInputType.Checkbox, "all", null));
			ths[0].CssProperties.Add("text-align", "left");
			if (m_CurrentVirtualDirectory != "/")
			{
				DirectoryInfo parent = Directory.GetParent(context.MapPath(m_CurrentVirtualDirectory));
				Tr tr = m_Files.AddTr();
				List<Td> tds = new List<Td>();
				for (int c = 0; c < m_Files.ColumnsCount; c++)
					tds.Add(tr.AddTd());
				tds[1].AddControl(new Img(string.Empty, "/Images/Icons/Ext/_folder.png"));
				tds[2].AddControl(new A("..", "?path=" + System.Web.HttpUtility.UrlEncode(parent.FullName.Substring(m_Files.RootPathLength).Replace("\\", "/"))));
			}
			foreach (string path in values)
			{
				if (Directory.Exists(path))
					m_Files.AddTr(new DirectoryInfo(path));
				else
					m_Files.AddTr(new FileInfo(path));
			}
		}

		public override void ProcessHtmlRequest(HttpHandlerContext context)
		{
			FileCommandHandler fileHandler = context.HandlerManager.GetHandler<FileCommandHandler>();
			fileHandler.DirectoryCreated += new OnDirectoryCreated(DirectoryCreated);
			fileHandler.FileDeleted += new OnFileDeleted(FileDeleted);

			if (context.Request["path"] != null)
				m_CurrentVirtualDirectory = context.Request["path"];
			if (string.IsNullOrWhiteSpace(m_CurrentVirtualDirectory))
				m_CurrentVirtualDirectory = "/";

			m_UploadForm.ServerSaveDirectory = m_CurrentVirtualDirectory;

			string rootPath = context.MapPath("/");
			m_Files.RootPathLength = rootPath.Length - 1;

			FillFromContext(context);

			base.ProcessHtmlRequest(context);
		}

		public override void ProcessJsRequest(HttpHandlerContext context)
		{
			base.ProcessJsRequest(context);

			string id = m_Files.Id;
			context.Js[id].AppendAjaxSubmit(m_DeleteFileForm, m_Delete);
			context.Js[id].AppendAjaxSubmit(m_CreateDirectoryForm, m_Create);
			context.Js[id].AppendAjaxHtmlHandler(m_Files, true, false);
			context.Js[id].AppendOnInit(@"
	$('#" + id + @"').on('click','input[name=""all""]',function() {
		var $this = $(this);
		if($this.is(':checked')) {
			$('#" + id + @" input[name=""files""]').attr('checked','checked');
		} else {
			$('#" + id + @" input[name=""files""]').removeAttr('checked');
		}
	});
");
		}

		void FileDeleted(HttpHandlerContext context)
		{
			FillFromContext(context);
			if (context.Json != null)
			{
				JsonObject table = new JsonObject();
				table.Add("html", m_Files.ToInnerHtml(context.Translator));
				context.Json.Add(m_Files.Id, table);
			}
		}

		void DirectoryCreated(HttpHandlerContext context)
		{
			FillFromContext(context);
			if (context.Json != null)
			{
				JsonObject table = new JsonObject();
				table.Add("html", m_Files.ToInnerHtml(context.Translator));
				context.Json.Add(m_Files.Id, table);
			}
		}
	}

	public class FileDataTable : DataTable<FileSystemInfo>
	{

		private int m_RootPathLength;

		public int RootPathLength { get { return m_RootPathLength; } set { m_RootPathLength = value; } }

		private static string GetIcon(FileSystemInfo value)
		{
			if (value is DirectoryInfo)
				return "_folder";
			if (value.Name.ToLowerInvariant().EndsWith(".html.xml"))
				return "_page";
			switch (value.Extension.ToLowerInvariant())
			{
				case ".html":
				case ".htm":
					return "html";
				case ".db":
					return "db";
				case ".dll":
					return "dll";
				case ".pdf":
					return "pdf";
				case ".css":
					return "css";
				case ".js":
					return "js";
				case ".xml":
					return "xml";
				case ".avi":
					return "avi";
				case ".gif":
					return "gif";
				case ".png":
					return "png";
				case ".bmp":
					return "bmp";
				case ".jpg":
				case ".jpeg":
					return "jpg";
				case ".mp3":
					return "mp3";
				case ".mp4":
					return "mp4";
				case ".mpg":
				case ".mpeg":
					return "mpg";
				case ".txt":
					return "txt";
				default:
					return "_blank";
			}
		}

		private long DirectoryLength(DirectoryInfo di)
		{
			long length = 0;
			foreach (FileInfo fi in di.GetFiles())
				length += fi.Length;
			foreach (DirectoryInfo sdi in di.GetDirectories())
				length += DirectoryLength(sdi);
			return length;
		}

		public override void Load(System.Xml.XmlNode node, HtmlControlLoader loader)
		{
			base.Load(node, loader);

			AddColumn(null, 20, delegate(FileSystemInfo value, Column column, int index) { return OneControl(new Input(HtmlInputType.Checkbox, "files", value.FullName.Substring(m_RootPathLength).Replace("\\", "/"))); }, null);
			AddColumn(null, 20, delegate(FileSystemInfo value, Column column, int index) { return OneControl(new Img(value.Extension, "/Images/Icons/Ext/" + GetIcon(value) + ".png")); }, null);
			AddColumn("Name", 300, delegate(FileSystemInfo value, Column column, int index)
			{
				if (value is FileInfo)
					return ParseHtml(value.Name + " (<a href=\"" + value.FullName.Substring(m_RootPathLength).Replace("\\", "/") + "\" target=\"_blank\" title=\"Download or view the file online.\">get</a>)");
				else
					return OneControl(new A(value.Name, "?path=" + System.Web.HttpUtility.UrlEncode(value.FullName.Substring(m_RootPathLength).Replace("\\", "/"))));
			}, null);
			AddColumn("Length", 100, delegate(FileSystemInfo value, Column column, int index)
			{
				if(value is FileInfo)
					return ParseText(StringExtensions.ToUserFriendlyByteLength((ulong)(value as FileInfo).Length));
				if(value is DirectoryInfo)
					return ParseText(StringExtensions.ToUserFriendlyByteLength((ulong)DirectoryLength(value as DirectoryInfo)));
				return ParseText("n/a");
			}, null);
			AddColumn("CreationTime", 180, delegate(FileSystemInfo value, Column column, int index) { return ParseText(value.CreationTime.ToString()); }, null);
			AddColumn("LastWriteTime", 180, delegate(FileSystemInfo value, Column column, int index) { return ParseText(value.LastWriteTime.ToString()); }, null);
		}

		public new Tr AddTr(FileSystemInfo value)
		{
			Tr tr = base.AddTr(value);
			return tr;
		}
	}
}
